package com.calvin.study.lock.viewlock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

import com.calvin.study.utils.Print;
import com.calvin.study.utils.ThreadUtil;

import lombok.Data;

public class CLHLock implements Lock {

	/**
	 * 当前节点的线程本地变量
	 */
	private static ThreadLocal<Node> curNodeLocal = new ThreadLocal<Node>();

	private String name;
	
	/**
	 * CLHLock队列的尾部指针， 使用AtomicReference， 方便进行CAS操作
	 */
	private AtomicReference<Node> tail = new AtomicReference<>(null);

	public CLHLock() {
		// 设置尾部节点
		tail.getAndSet(Node.EMPTY);
	}

	public CLHLock(String name) {
		this.name = name;
		// 设置尾部节点
		tail.getAndSet(Node.EMPTY);
	}

	// 加锁：将节点添加到等待队列的尾部
	@Override
	public void lock() {
		//当线程X开始执行了Lock操作，创建一个节点，locked状态为True
		Node curNode = new Node(true, null);
		//获取当前队尾节点
		Node preNode = tail.get();
		// CAS自旋：将当前节点（curNode）插入到队列的尾部 
		//cas自旋 第一次，判断当前节点与期望值是否相符node（false，null），然后将内存值更新为node（true，null）
		while (!tail.compareAndSet(preNode, curNode)) {
			preNode = tail.get();
		}
		// 设置前驱
		curNode.setPrevNode(preNode);

		// 监听前驱节点的locked变量，直到其值为false
		// 若前继节点的locked状态为true，则表示前一线程还在抢占或者占有锁
		while (curNode.getPrevNode().isLocked()) {
			// 让出CPU时间片，提高性能
			// Thread.yield();
			ThreadUtil.yieldThread();

		}
		// 能执行到这里，说明当前线程获取到了锁
		// Print.tcfo("获取到了锁！！！");

		// 设置在线程本地变量中，用于释放锁
		curNodeLocal.set(curNode);
	}

	@Override
	public void unlock() {
		Node curNode = curNodeLocal.get();
		curNode.setPrevNode(null);// help for GC
		curNodeLocal.set(null);
		curNode.setLocked(false);
	}

	@Data
	static class Node {
		// true：当前线程正在抢占锁、或者已经占有锁
		// false：当前线程已经释放锁，下一个线程可以占有锁了
		volatile boolean locked;
		// 前一个节点，需要监听其locked字段
		Node prevNode;

		// 空节点
		public static final Node EMPTY = new Node(false, null);

		public Node(boolean locked, Node prevNode) {
			this.locked = locked;
			this.prevNode = prevNode;
		}
	}

	@Override
	public void lockInterruptibly() throws InterruptedException {
		throw new IllegalStateException("方法 'lockInterruptibly' 尚未实现!");
	}

	@Override
	public boolean tryLock() {
		throw new IllegalStateException("方法 'tryLock' 尚未实现!");

	}

	@Override
	public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
		throw new IllegalStateException("方法 'tryLock' 尚未实现!");
	}

	@Override
	public Condition newCondition() {
		throw new IllegalStateException("方法 'newCondition' 尚未实现!");
	}

	@Override
	public String toString() {
		return "CLHLock{" + name + '}';
	}
}
